/*
 * EmulatedNetworkSocket.cpp
 *
 *  Created on: Mar 17, 2011
 *      Author: sms103
 */

#include "EmulatedNetworkSocket.h"

EmulatedNetworkSocket::EmulatedNetworkSocket(SFTPTransportSocket *sftpTranspSock, TCPSocket *tcpSock) {
	transpSock = sftpTranspSock;
	packetSendBuf = malloc(transpSock->getTransportMSS()+ sizeof(STCPHeader) + sizeof(uint32_t));
	bufferedBytes=0;
	bufferedPackets = 0;
	paramParser = transpSock->paramParser;

	memset(&stats,0,sizeof(network_stats));
	setParams();

	//initialize the RNG
	struct timeval tv;
	gettimeofday(&tv,NULL);
	mtrng.init(tv.tv_usec);

	//fixed init for repeatability
	//unsigned long long init[4] = {0x12345ULL, 0x23456ULL, 0x34567ULL, 0x45678ULL};
	//size_t length = 4;
	//mt.init(init, length);

	this->tcpSock = tcpSock;

}

//
bool EmulatedNetworkSocket::networkSend(void *packet,size_t len){
	STCPHeader *hdr = (STCPHeader *)packet;
	/*if(hdr->th_flags & TH_ACK){
		cout << "NET SEND ACK "<<ntohl(hdr->th_ack)<<endl;
	}
	else cout << "NET SEND SEQ "<<ntohl(hdr->th_seq)<<endl;
	*/
	//make a copy of the packet so i can put errors in it
	//the + uint32t is so there is room to prepend a 32 bit length.
	memcpy((uint8_t *)packetSendBuf + (sizeof(uint32_t)) , packet, len);

	//see if we should drop this packet or introduce a bit error, according
	//to random chance and our rates of bit error and dropping
	stats.totalBytesSent+=len;
	stats.totalPacketsSent++;
	//decide to drop or not
	if(dropThis()){
		stats.packetsDropped++;
		return true;
	}

	//decided about flipping bits (introducing errors)
	uint32_t sectionSize = 1000;//we will use 1000 byte sections and decide for each
								//of these sections whether to flip a bit.
	uint32_t currSection = 0;
	bool errorCaused = false;
	while(currSection * sectionSize + sectionSize < len){
		if(causeError((uint8_t *)packetSendBuf + (sizeof(uint32_t)) + (currSection*sectionSize), sectionSize)){
			errorCaused = true;
		}
		currSection++;
	}

	//get last leftover section
	if(len - (currSection * sectionSize) > 0){
		//cout << "sending packet at network layer of size "<<len<<endl;
		if(causeError((uint8_t *)packetSendBuf + (sizeof(uint32_t)) + (currSection*sectionSize), len - (currSection * sectionSize))){
			errorCaused = true;
		}
	}
	if(errorCaused){

		stats.errorsCaused++;
		cout << "Errors caused: "<< stats.errorsCaused<<endl;
	}
	//else cout <<"Sending packet without error"<<endl;

	//insert the length as a header
	*(uint32_t *)packetSendBuf = htonl((uint32_t)len);

	size_t bytesSent = 0;
	len += 4;//4 extra bytes for the 32 bit length im prepending
	int retval;
	while(bytesSent < len){
		retval = tcpSock->tcpSend((uint8_t *)packetSendBuf +bytesSent,len-bytesSent);
		if (retval <= 0){
			return false;
		}
		bytesSent +=retval;

	}
	return true;
}

void EmulatedNetworkSocket::close(){
	tcpSock->tcpClose();
	//cout <<"sock closed from app"<<endl;
}

bool EmulatedNetworkSocket::startRecvThread(){
	int retval = pthread_create(&this->networkRecvThread, NULL,
			EmulatedNetworkSocket::runNetworkRecvThread, (void *)this);
	if (retval < 0) return false;
	return true;
}


void * EmulatedNetworkSocket::runNetworkRecvThread(void *p){
	EmulatedNetworkSocket * netSocket= (EmulatedNetworkSocket *)p;
	while(1){
		if(netSocket->recvPacket() <= 0){
			break;
		}
	}
}

//returns true if chance says the network will drop this packet.
//uses the networkDropRate and our RNG to decide.
bool EmulatedNetworkSocket::dropThis(){
	if(mtrng.getDouble() < this->networkDropRate){
		return true;
	}
	else return false;
}


//flips a bit in the specified memory region if the hasError function says there
//should be an error in that many bits. Returns true if an error was introduced,
//returns false otherwise
bool EmulatedNetworkSocket::causeError(uint8_t *start, uint32_t len){
	//cout << "causing error maybe"<<endl;
	if(hasError((uint64_t)len*8)){//multiply by 8 to get bits instead of bytes
		//pick the random bit in the packet
		uint32_t victimBit = mtrng.getUint()%(len*8);
		uint32_t victimByte = victimBit/8;
		victimBit = victimBit % 8;
		uint8_t victimMask = 1 << victimBit;

		//flip the bit with XOR
		*(start+victimByte) = *(start+victimByte) ^ victimMask;
		return true;
	}
	else return false;
}

//returns true if chance says there is an error in this group of numBits bits
//uses the networkBitErrorRate and our RNG to decide
bool EmulatedNetworkSocket::hasError(uint64_t numBits){
	if(networkBitErrorRate == 0)return false;
	double probabilityOfError = 1- pow((1-networkBitErrorRate),(double)numBits);
	//cout << "ErrRate: "<<networkBitErrorRate << " prob of error with "<<numBits<<" bits: "<<probabilityOfError<<endl;
	double roll = mtrng.getDouble();
	if( roll < probabilityOfError){
		//cout << "rolled "<<roll<<" returning error."<<endl;
		return true;
	}
	else return false;
}

int EmulatedNetworkSocket::recvPacket(){
	uint32_t len;
	int retval;
	retval = tcpSock->tcpRecv((void *)&len,sizeof(len));
	len = ntohl(len);
	if (retval <= 0){
		pthread_mutex_lock(&transpSock->dataReadyLock);
		queue_entry * entry = (queue_entry *)malloc(sizeof(queue_entry));

		entry->packet=NULL;
		entry->packetLen = 0;
		//cout << "queueing entry: "<<hex<<(uint64_t)entry<< " packet addr: "<<(uint64_t)entry->packet<<endl;
		fromNetworkDataQueue.push(entry);
		pthread_mutex_unlock(&transpSock->dataReadyLock);
		pthread_cond_broadcast(&transpSock->dataReadyCond);
		return retval;
	}
	else{
		pthread_mutex_lock(&transpSock->dataReadyLock);
		void *packet = (void *)malloc(len);
		size_t bytesRcvd = 0;
		while(bytesRcvd < len){
			retval = tcpSock->tcpRecv((char *)packet+bytesRcvd,len-bytesRcvd);
			if(retval <= 0){
				//cout <<"free'ing : "<<hex<<(uint64_t)packet<<endl;
				free(packet);
				return retval;
			}
			bytesRcvd+=retval;
		}
		if(bytesRcvd > 0){
		queue_entry * entry = (queue_entry *)malloc(sizeof(queue_entry));
		STCPHeader *hdr = (STCPHeader *)packet;
		/*if(hdr->th_flags & TH_ACK){
			cout << "NET RECV ACK "<<ntohl(hdr->th_ack)<<endl;
		}
		else cout << "NET RCV SEQ "<<ntohl(hdr->th_seq)<<endl;
		*/
		entry->packet=packet;
		entry->packetLen = len;
		while((len+bufferedBytes > NETWORK_BUFFER_SIZE && !fromNetworkDataQueue.empty()) || bufferedPackets >= NETWORK_BUFFER_PACKET_LIMIT){
			pthread_cond_wait(&transpSock->dataReadyCond,&transpSock->dataReadyLock);
		}
		//cout << "queueing entry: "<<hex<<(uint64_t)entry<< " packet addr: "<<(uint64_t)entry->packet<<endl;
		bufferedBytes+= len;
		bufferedPackets ++;
		fromNetworkDataQueue.push(entry);
		}
		pthread_mutex_unlock(&transpSock->dataReadyLock);
		pthread_cond_broadcast(&transpSock->dataReadyCond);

	}
	return retval;

}


void EmulatedNetworkSocket::setParams(){
	//set bit error rate
	paramParser->setDoubleParam(&networkBitErrorRate,"networkBitErrorRate",DEFAULT_NETWORK_BIT_ERROR_RATE);

	//set rate at which network drops packets
	paramParser->setDoubleParam(&networkDropRate,"networkDropRate",DEFAULT_NETWORK_DROP_RATE);

}

network_stats * EmulatedNetworkSocket::getNetworkStats(){
	return &this->stats;
}

void EmulatedNetworkSocket::clearStats(){
	memset(&stats,0,sizeof(network_stats));
}

double EmulatedNetworkSocket::getErrorRate(){
	return this->networkBitErrorRate;
}

EmulatedNetworkSocket::~EmulatedNetworkSocket() {
	close();
	delete(this->tcpSock);
	free(packetSendBuf);
	pthread_join(this->networkRecvThread,NULL);
	while(!fromNetworkDataQueue.empty()){
		if(fromNetworkDataQueue.front()->packet)free(fromNetworkDataQueue.front()->packet);
		free(fromNetworkDataQueue.front());
		fromNetworkDataQueue.pop();
	}

}
